//
//  HLELowcostScheduler.m
//  HLELowcostPeripherial
//
//  Created by hle1 on 2021/5/4.
//

#import "HLELowcostScheduler.h"
#import "HLEAdvertiser.h"
#import "HLELowcostPeripherialHeader.h"
#import "HLELowcostUtil.h"
#import <YYCategories/YYCategories.h>
#import <YsToolsManager/YsToolsManager.h>
#import <ReactiveObjC/ReactiveObjC.h>
#import <HLELowcostBleManager.h>


typedef struct {
     char reserved[2];
     char serialNum[16];
     char version[10];
     char bind;
} BLE_ADV_INFOR;

static NSOperationQueue *HLELowcostSchedulerAdvertiserQueue_;
static NSInteger  kHLELowcostPeripherialTimeOut = 0;
static NSInteger  kHLELowcostPeripherialTimeListTimeOut = 0;
static NSString * const kHLELowcostPeripherialName = @"MANKA-LED-STRIP";


@implementation HLELowcostScheduler

#pragma mark - LifeCycle

+ (void)initialize
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        HLELowcostSchedulerAdvertiserQueue_ = [[NSOperationQueue alloc]init];
        HLELowcostSchedulerAdvertiserQueue_.maxConcurrentOperationCount = 1;
        HLELowcostSchedulerAdvertiserQueue_.qualityOfService = NSQualityOfServiceUtility;
        HLELowcostSchedulerAdvertiserQueue_.suspended = NO;
    });
}

+ (void)initLowcostDeviceSDK:(NSInteger)appendFlagTimeout normalTimeout:(NSInteger)normalTimeout {
    __unused HLEAdvertiser *av =  HLEAdvertiser.sharedInstance;
    kHLELowcostPeripherialTimeListTimeOut = appendFlagTimeout;
    kHLELowcostPeripherialTimeOut = normalTimeout;
}

#pragma mark - Public

+ (BOOL)isLowcostWithAdvertisementData:(NSDictionary *)advertisementData
{
    if ([advertisementData[@"kCBAdvDataLocalName"] isEqualToString:kHLELowcostPeripherialName]) {
        return YES;
    }else{
        return NO;
    }
}

+ (NSString *)getSerialNumWithAdvertisementData:(NSDictionary *)advertisementData
{
    if ([advertisementData[@"kCBAdvDataLocalName"] isEqualToString:kHLELowcostPeripherialName]) {
        NSData *advDataManufacturerData = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
        if (advDataManufacturerData.length >= 28) {
            BLE_ADV_INFOR *info = (BLE_ADV_INFOR *)advDataManufacturerData.bytes;
            NSData *data = [NSData dataWithBytes:info->serialNum length:sizeof(info->serialNum)];
            return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }else{
            return @"";
        }
    }else{
        return @"";
    }
}

+ (NSString *)getVersionNumWithAdvertisementData:(NSDictionary *)advertisementData
{
    if ([advertisementData[@"kCBAdvDataLocalName"] isEqualToString:kHLELowcostPeripherialName]) {
        NSData *advDataManufacturerData = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
        BLE_ADV_INFOR *info = (BLE_ADV_INFOR *)advDataManufacturerData.bytes;
        if (advDataManufacturerData.length >= 28) {
            NSData *data = [NSData dataWithBytes:info->version length:sizeof(info->version)];
            return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }else{
            return @"";
        }
    }else{
        return @"";
    }
}

+ (BOOL)isUnBindWithAdvertisementData:(NSDictionary *)advertisementData
{
    if ([advertisementData[@"kCBAdvDataLocalName"] isEqualToString:kHLELowcostPeripherialName]) {
        NSData *advDataManufacturerData = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
        BLE_ADV_INFOR *info = (BLE_ADV_INFOR *)advDataManufacturerData.bytes;
        if (advDataManufacturerData.length >= 28) {
            if (info->bind == 'R') {
                return YES;
            }else{
                return NO;
            }
        }else{
            return NO;
        }
    }else{
        return NO;
    }
}


+ (NSString *)getLowcostDeviceCodeWithAdvertisementData:(NSDictionary *)advertisementData
{
    if ([self isLowcostWithAdvertisementData:advertisementData]) {
        uint8_t *dataByte =  [self parseLowcostDataWithAdvertisementData:advertisementData];
        NSString *str = [NSString stringWithFormat:@"%02x",dataByte[3]];
        if ([str isEqualToString:@"01"]) {
            return [NSString stringWithFormat:@"%02x%02x%02x%02x",dataByte[4],dataByte[5],dataByte[6],dataByte[7]];
        }
        return @"";
    }else{
        return @"";
    }
}

+ (void)setDeviceConnectBySerialNum:(NSString *)serialNum
                           complete:(void (^)(BOOL isSuccess,NSData *data))complete
{
    S_START_CONNECT_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = 0x00;
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:@[] delay:0.8 serialNum:serialNum complete:complete];
}

+ (void)setDeviceDisConnectBySerialNum:(NSString *)serialNum
                                 delay:(CGFloat)delay
{
    [HLELowcostBleManager.sharedInstance disconnectBySerialNum:serialNum];
}

+ (void)cancelAllPeripheralsConnection
{
    [HLELowcostBleManager.sharedInstance cancelAllPeripheralsConnection];
}

+ (void)setDeviceModeWithDeviceCode:(NSString *)deviceCode
                               mode:(NSInteger)mode
                      scene_mode_id:(NSInteger)scene_mode_id
                     RGB_brightness:(NSInteger)RGB_brightness
                          RGB_speed:(NSInteger)RGB_speed
                            RGB_cct:(NSInteger)RGB_cct
                          colorList:(nullable NSArray <UIColor *>*)colorList
                            isGroup:(BOOL)isGroup
                              delay:(CGFloat)delay
                     switching_mode:(NSInteger)switching_mode
                     extra_scene_id:(NSInteger)extra_scene_id                                 deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                          serialNum:(NSString *)serialNum
                           complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    NSParameterAssert(deviceCode.length >= 4);
    
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    S_CMD_SET_DEV_MODE_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_DEV_MODE_REQ;
    
    /// 附加包
    S_CMD_SET_DEV_MODE_APPEND_REQ appendReq = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    appendReq.header.command = CMD_SET_DEV_MODE_APPEND_REQ;
    
    
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    
    appendReq.dev_rolling_code[0] = byteData[0];
    appendReq.dev_rolling_code[1] = byteData[1];
    appendReq.dev_rolling_code[2] = byteData[2];
    appendReq.dev_rolling_code[3] = byteData[3];
    REC.scene_mode_id = scene_mode_id + (mode << 4);
    REC.RGB_brightness = RGB_brightness;
    REC.RGB_speed = CFSwapInt16BigToHost((UInt16)RGB_speed);
    REC.RGB_sw_mode = switching_mode;
    REC.extra_scene_id = extra_scene_id;
    
    if (colorList.count == 1) {
        if (mode == MODE_ID_CW_CCT  && scene_mode_id ==  SCENE_ID_COMMON) {
            REC.RGB_brightness = 0;
            REC.RGB_value0[0] = RGB_brightness;
            if (RGB_brightness == -1) {
                REC.RGB_value0[0] = 0xFF;
            }
            REC.RGB_value0[1] = RGB_cct;
            if (RGB_cct == -1) {
                REC.RGB_value0[1] = 0xFF;
            }
        }else{
            REC.RGB_value0[0] = colorList.firstObject.red * 255;
            REC.RGB_value0[1] = colorList.firstObject.green * 255;
            REC.RGB_value0[2] = colorList.firstObject.blue * 255;
        }
        REC.append_flag = 0;
        [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:nil];
    }else if (colorList.count > 1){
        if (mode == MODE_ID_CW_CCT  && scene_mode_id ==  SCENE_ID_COMMON) {
            REC.RGB_brightness = 0;
            REC.RGB_value0[0] = RGB_brightness;
            if (RGB_brightness == -1) {
                REC.RGB_value0[0] = 0xFF;
            }
            REC.RGB_value0[1] = RGB_cct;
            if (RGB_cct == -1) {
                REC.RGB_value0[1] = 0xFF;
            }
        }else{
            REC.RGB_value0[0] = colorList.firstObject.red * 255;
            REC.RGB_value0[1] = colorList.firstObject.green * 255;
            REC.RGB_value0[2] = colorList.firstObject.blue * 255;
        }
        REC.append_flag = 1;
        [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:nil];
        
        NSInteger total_package = (colorList.count - 1) / 2 + (colorList.count - 1) % 2;
        appendReq.total_package = total_package;
        NSInteger j = 0;
        for (NSInteger i = 1; i < colorList.count; i += 2) {
            j = j + 1;
            NSInteger current_package = j;
            appendReq.curr_package_index = current_package;
            appendReq.RGB_value1[0] = colorList[i].red * 255;
            appendReq.RGB_value1[1] = colorList[i].green * 255;
            appendReq.RGB_value1[2] = colorList[i].blue * 255;
            
            UIColor *color2  =  (UIColor *)[colorList objectOrNilAtIndex:i + 1];
            appendReq.RGB_value2[0] = color2.red * 255;
            appendReq.RGB_value2[1] = color2.green * 255;
            appendReq.RGB_value2[2] = color2.blue * 255;
            if (i + 2 < colorList.count) {
                [self startAdvertiser:[NSData dataWithBytes:(Byte *)&appendReq length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:nil];
            }else{
                [self startAdvertiser:[NSData dataWithBytes:(Byte *)&appendReq length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:complete];
            }
        }
    }else{
        if (mode == MODE_ID_CW_CCT  && scene_mode_id ==  SCENE_ID_COMMON) {
            REC.RGB_brightness = 0;
            REC.RGB_value0[0] = RGB_brightness;
            if (RGB_brightness == -1) {
                REC.RGB_value0[0] = 0xFF;
            }
            REC.RGB_value0[1] = RGB_cct;
            if (RGB_cct == -1) {
                REC.RGB_value0[1] = 0xFF;
            }
        }
        [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH]  deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:complete];
    }
}


+ (void)setDeviceGroupIDWithDeviceCode:(NSString *)deviceCode
                               groupID:(NSString *)groupID
                                 delay:(CGFloat)delay
                             serialNum:(NSString *)serialNum
                              complete:(void (^)(BOOL isSuccess,NSData *data))complete
{
    NSParameterAssert(deviceCode.length == 8);
    NSParameterAssert(groupID.length == 4);
    
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    S_CMD_SET_DEV_GROUP_ID_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
//    REC.header.command = CMD_SET_DEV_GROUP_ID_REQ;
    
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    
    REC.group_id = CFSwapInt16BigToHost([NSString numberWithHexString:groupID]);
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:@[] delay:delay serialNum:serialNum complete:complete];
}

+ (void)setDeviceOpenLightStatu:(NSString *)deviceCode
                        isGroup:(BOOL)isGroup
                           mode:(NSInteger)mode
                    default_RGB:(UIColor *)default_RGB
                 default_cw_cct:(NSInteger)default_cw_cct
                 default_cw_lum:(NSInteger)default_cw_lum
                          delay:(CGFloat)delay
                    deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                      serialNum:(NSString *)serialNum
                       complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_SET_OPEN_LIGHT_STATU_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_OPEN_LIGHT_STATU_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    dev_open_light_statu_t statu = {0};
    statu.mode = mode;
    if (default_RGB) {
        statu.default_RGB[0] = default_RGB.red * 255;
        statu.default_RGB[1] = default_RGB.green * 255;
        statu.default_RGB[2] = default_RGB.blue * 255;
    }
    statu.default_cw_cct = default_cw_cct;
    statu.default_cw_lum = default_cw_lum;
    REC.status = statu;
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:complete];
}

+ (void)getDeviceDynamicMode:(NSString *)deviceCode
                     isGroup:(BOOL)isGroup
                       delay:(CGFloat)delay
                 deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                   serialNum:(NSString *)serialNum
                    complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_CMD_GET_DEV_MODE_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_GET_DEV_MODE_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:serialNum complete:complete];
}

+ (void)setDeviceBind:(NSString *)deviceCode
                delay:(CGFloat)delay
            serialNum:(NSString *)serialNum
             complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_SET_BIND_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_BIND_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dvc[0] = 0xff;
    REC.dvc[1] = 0xff;
    REC.dvc[2] = 0xff;
    REC.dvc[3] = 0xff;
    REC.magic[0] = 0x43;
    REC.magic[1] = 0x4d;
    REC.magic[2] = 0x75;
    REC.magic[3] = 0x21;
    REC.nrc[0] = byteData[0];
    REC.nrc[1] = byteData[1];
    REC.nrc[2] = byteData[2];
    REC.nrc[3] = byteData[3];
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:@[] delay:delay serialNum:serialNum complete:complete];
}

+ (void)setDeviceReset:(NSString *)deviceCode
                 delay:(CGFloat)delay
             serialNum:(NSString *)serialNum
              complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_SET_BIND_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_BIND_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dvc[0] = byteData[0];
    REC.dvc[1] = byteData[1];
    REC.dvc[2] = byteData[2];
    REC.dvc[3] = byteData[3];
    REC.magic[0] = 0x65;
    REC.magic[1] = 0x38;
    REC.magic[2] = 0x79;
    REC.magic[3] = 0x25;
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:@[] delay:delay serialNum:serialNum complete:complete];
}

+ (void)setDeviceMarQuee:(NSString *)deviceCode
               nodeCount:(NSInteger)nodeCount
                  rgbSeq:(NSInteger)rgbSeq
                protocol:(NSInteger)protocol
                   delay:(CGFloat)delay
               serialNum:(NSString *)serialNum
                complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_SET_MARQUEE_CFG_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_MARQUEE_CFG_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dvc[0] = byteData[0];
    REC.dvc[1] = byteData[1];
    REC.dvc[2] = byteData[2];
    REC.dvc[3] = byteData[3];
    REC.nodeCount = CFSwapInt16BigToHost(nodeCount);
    REC.rgbSeq = rgbSeq;
    REC.protocol = protocol;
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:@[]  delay:delay serialNum:serialNum complete:complete];
}

+ (void)setDeviceGroupDissolveWithGroupID:(NSString *)groupID
                                    delay:(CGFloat)delay
                                serialNum:(NSString *)serialNum
                                 complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    NSParameterAssert(groupID.length == 4);
    S_CMD_DISSOLVE_DEV_GROUP_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
//    REC.header.command = CMD_DISSOLVE_DEV_GROUP_REQ;
    REC.group_id = CFSwapInt16BigToHost([NSString numberWithHexString:groupID]);
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:@[]  delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)setDeviceCountDown:(NSString *)deviceCode
                   isGroup:(BOOL)isGroup
                 countDown:(NSInteger)countDown
                    action:(NSInteger)action
                     delay:(CGFloat)delay
               deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                 serialNum:(NSString *)serialNum
                  complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_CMD_SET_DEV_COUNT_DOWN_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_DEV_COUNT_DOWN_REQ;
    
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    
    RGBCount_down_t task = {0};
    task.count_down_time = CFSwapInt32BigToHost((UInt32)countDown);
    task.action = action;
    REC.task = task;
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)getDeviceCountDown:(NSString *)deviceCode
                   isGroup:(BOOL)isGroup
                     delay:(CGFloat)delay
               deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                 serialNum:(NSString *)serialNum
                  complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_CMD_SET_DEV_COUNT_DOWN_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_GET_DEV_COUNT_DOWN_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)setDeviceTime:(NSString *)deviceCode
              isGroup:(BOOL)isGroup
                delay:(CGFloat)delay
            timestamp:(NSInteger)timestamp
          deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
            serialNum:(NSString *)serialNum
             complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_CMD_SET_DEV_TIME_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_DEV_TIME_REQ;
    
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    REC.timestamp = CFSwapInt32BigToHost((UInt32)timestamp);
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)setDeviceTiming:(NSString *)deviceCode
                isGroup:(BOOL)isGroup
                 taskID:(NSInteger)taskID
                  state:(NSInteger)state
                   mode:(NSInteger)mode
                 repeat:(NSInteger)repeat
                 action:(NSInteger)action
                  start:(NSInteger)start
                    end:(NSInteger)end
                  delay:(CGFloat)delay
            deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
              serialNum:(NSString *)serialNum
               complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_CMD_SET_DEV_TIMING_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_SET_DEV_TIMING_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    RGBTimer_t time  = {0};
    time.combined_var = taskID + (state << 7);
    time.mode = mode;
    time.repeat = repeat;
    time.action = action;
    time.start_s = CFSwapInt16BigToHost((UInt16)start);
    time.end_s = CFSwapInt16BigToHost((UInt16)end);
    REC.tmr = time;
    
    if (action == 3) { /// 删除
        memset(&(REC.tmr), 0, 8);
        REC.tmr.combined_var = taskID;
        REC.tmr.action = action;
    }
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)getDeviceTiming:(NSString *)deviceCode
                isGroup:(BOOL)isGroup
                  delay:(CGFloat)delay
            deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
              serialNum:(NSString *)serialNum
               complete:(nullable void (^)(BOOL isSuccess,id data))complete
{
    S_CMD_GET_DEV_TIMING_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_GET_DEV_TIMING_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)getOpenLightStatu:(NSString *)deviceCode
                  isGroup:(BOOL)isGroup
                    delay:(CGFloat)delay
              deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                serialNum:(NSString *)serialNum
                 complete:(nullable void (^)(BOOL isSuccess,NSData *data))complete
{
    S_GET_OPEN_LIGHT_STATU_REQ REC = MINI_BLUETOOTH_TAPE_LIGHT_HEADER_RX;
    REC.header.command = CMD_GET_OPEN_LIGHT_STATU_REQ;
    uint8_t *byteData = (uint8_t *)[[NSData dataWithHexString:deviceCode] bytes];
    REC.dev_rolling_code[0] = byteData[0];
    REC.dev_rolling_code[1] = byteData[1];
    REC.dev_rolling_code[2] = byteData[2];
    REC.dev_rolling_code[3] = byteData[3];
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)&REC length:CONF_PAYLOAD_WIDTH] deviceCodes:deviceCodes delay:delay serialNum:(NSString *)serialNum complete:complete];
}

+ (void)debugMsg:(NSData *)data serialNum:(NSString *)serialNum
{
    [self startAdvertiser:[NSData dataWithBytes:(Byte *)data.bytes length:CONF_PAYLOAD_WIDTH] deviceCodes:@[] delay:2 serialNum:serialNum complete:nil];
}

+ (void)cancelAllOperations
{
    [HLEAdvertiser.sharedInstance stop];
    [HLELowcostSchedulerAdvertiserQueue_ cancelAllOperations];
    HLELowcostSchedulerAdvertiserQueue_.suspended = NO;
}

#pragma mark - private

+ (uint8_t *)parseLowcostDataWithAdvertisementData:(NSDictionary *)advertisementData
{
    NSData *advDataManufacturerData = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
    if (advDataManufacturerData.hexString.isNotBlank && advDataManufacturerData.hexString.length > 20) {
        NSString *tempStr = [advDataManufacturerData.hexString substringFromIndex:4];
        uint8_t *dataBytes =(uint8_t *)[[NSData dataWithHexString:tempStr] bytes];
        return dataBytes;
    }else{
        return nil;
    }
}

+ (void)HLELowcostPeripherialLogMsg:(unsigned char *)payload serialNum:(NSString *)serialNum
{
    NSMutableString *str = [[NSMutableString alloc] init];
    for (int i = 0; i < 20; i++) {
        [str appendString:[NSString stringWithFormat:@"%02X " ,payload[i]]];
        if (i % 5 == 4) {
            [str appendString:@"\n"];
        }
    }
    NSLog(@"HLELowcostPeripherial  serialNum:%@:\n%@\n",serialNum,str);
}

+ (void)startByte:(NSData *)REC serialNum:(NSString *)serialNum

{
    [self HLELowcostPeripherialLogMsg:(unsigned char *)REC.bytes serialNum:serialNum];
    [HLELowcostBleManager.sharedInstance sendMsgBySerialNum:serialNum data:REC complete:nil];
}

+ (void)startByte:(NSData *)REC
      deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
        serialNum:(NSString *)serialNum
         complete:(void (^) (BOOL isSuccess,id data))complete
{
    [self HLELowcostPeripherialLogMsg:(unsigned char *)REC.bytes serialNum:serialNum];
    [HLELowcostBleManager.sharedInstance sendMsgBySerialNum:serialNum data:REC complete:complete];
}

+ (void)startAdvertiser:(NSData *)REC
            deviceCodes:(nullable NSArray <NSString *>*)deviceCodes
                  delay:(CGFloat)delay
              serialNum:(NSString *)serialNum
               complete:(void (^)(BOOL isSuccess,id data))complete
{
    [HLELowcostSchedulerAdvertiserQueue_ addOperationWithBlock:^{
        __block BOOL isexcute = NO;
        void (^stopBlcok)(void) = ^ {
            HLELowcostSchedulerAdvertiserQueue_.suspended = NO;
            isexcute = YES;
        };
        
        if (complete) {
            [self startByte:REC deviceCodes:deviceCodes serialNum:serialNum complete:^(BOOL isSuccess, id data) {
                stopBlcok();
                complete(isSuccess,data);
            }];
        }else{
            [RACScheduler.mainThreadScheduler afterDelay:delay schedule:^{
                stopBlcok();
            }];
            [self startByte:REC serialNum:serialNum];
        }
        HLELowcostSchedulerAdvertiserQueue_.suspended = YES;
    }];
}
@end
